package com.nineclock.auth.config;

import com.alibaba.fastjson.JSON;
import com.nineclock.auth.intergration.entity.IntergrationAuthenticationEntity;
import com.nineclock.auth.intergration.processor.AuthenticationProcessor;
import com.nineclock.auth.intergration.threadLocal.IntergrationAuthenticationHolder;
import com.nineclock.auth.intergration.threadLocal.UserHolder;
import com.nineclock.common.constant.NcConstant;
import com.nineclock.common.entity.UserInfo;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysRoleDTO;
import com.nineclock.system.dto.SysUserDTO;
import com.nineclock.system.feign.SysCompanyUserFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired // 将容器中所有该类型的bean注入到list中
    private List<AuthenticationProcessor> processorList;

    @Autowired
    private SysCompanyUserFeign companyUserFeign;


    //明文密码为 123456
    @Override
    public UserDetails loadUserByUsername(String username) {
        // 从threadLocal中取出entity
        IntergrationAuthenticationEntity entity = IntergrationAuthenticationHolder.get();

        // 根据entity中的参数挑选认证处理器
        AuthenticationProcessor processor = chooseAuthenticationProcessor(entity);
        if (processor == null) {
            throw new NcException(ResponseEnum.SERVER_ERROR);
        }

        // 调用认证处理器进行认证处理
        SysUserDTO userDTO = processor.authenticate(entity);

        // ------------认证功能完善----start
        // 获取客户端id
        String clientId = entity.getAuthParameter("client_id");
        String companyIdStr = entity.getAuthParameter("company_id");
        Long companyId = StringUtils.isEmpty(companyIdStr) ? null : Long.valueOf(companyIdStr);


        UserInfo userInfo = BeanHelper.copyProperties(userDTO, UserInfo.class);


        if (clientId.equals("pc_client")) { // pc端认证

            // 调用系统微服务获取用户对应员工信息
            List<SysCompanyUserDTO> companyUserDTOS = companyUserFeign.queryCompanyUser(userDTO.getId(), companyId).getData();

            if (CollectionUtils.isEmpty(companyUserDTOS)) {
                throw new NcException(ResponseEnum.USER_NOT_JOIN_COMPANY);
            }

            // 判断是否是管理员，非管理员不能登录
            Boolean isAdmin = checkAdmin(companyUserDTOS);
            if (!isAdmin) {
                throw new NcException(ResponseEnum.USER_NOT_COMPANY_ADMIN);
            }

            // 设置员工信息，必须只有一条员工信息 或 企业id不为空
            if (companyUserDTOS.size() == 1 || companyId != null) {
                SysCompanyUserDTO companyUserDTO = companyUserDTOS.get(0);
                // 封装员工信息
                wrapper(companyUserDTO, userInfo);
            }

        } else { // app端

            // 如果没有传递企业id，就使用最后一次登录的企业id
            if(companyId == null){
                companyId = userDTO.getLastLoginCompanyId();
            }

            // 调用系统微服务获取用户对应员工信息
            List<SysCompanyUserDTO> companyUserDTOS = companyUserFeign.queryCompanyUser(userDTO.getId(), companyId).getData();

            if (CollectionUtils.isEmpty(companyUserDTOS)) {
                throw new NcException(ResponseEnum.USER_NOT_JOIN_COMPANY);
            }

            SysCompanyUserDTO companyUserDTO = companyUserDTOS.get(0);

            // 封装员工信息
            wrapper(companyUserDTO, userInfo);

        }


        // ------------认证功能完善----end


        // 将userInfo放到threadLocal中
        UserHolder.set(userInfo);

        return User.withUsername(JSON.toJSONString(userInfo)).password(userDTO.getPassword())
                .authorities(userInfo.getGrantedAuthorities()).build();
    }

    /**
     * 组装用户的员工信息
     *
     * @param sysCompanyUserDTO
     * @param userInfo
     */
    private void wrapper(SysCompanyUserDTO sysCompanyUserDTO, UserInfo userInfo) {
        userInfo.setCompanyId(sysCompanyUserDTO.getCompanyId());
        userInfo.setCompanyName(sysCompanyUserDTO.getCompanyName());
        userInfo.setDepartmentId(sysCompanyUserDTO.getDepartmentId());
        userInfo.setDepartmentName(sysCompanyUserDTO.getDepartmentName());
        userInfo.setCompanyUserId(sysCompanyUserDTO.getId());
        userInfo.setPost(sysCompanyUserDTO.getPost());
        userInfo.setWorkNumber(sysCompanyUserDTO.getWorkNumber());
        userInfo.setTimeEntry(sysCompanyUserDTO.getTimeEntry());
        userInfo.setEnable(true);

        // 用户的权限
        if (!CollectionUtils.isEmpty(sysCompanyUserDTO.getGrantedAuthorities())) {
            userInfo.setGrantedAuthorities(sysCompanyUserDTO.getGrantedAuthorities());
        }
    }

    /**
     * 判断员工是否是管理员角色
     *
     * @param companyUserDTOS
     * @return
     */
    private Boolean checkAdmin(List<SysCompanyUserDTO> companyUserDTOS) {

        for (SysCompanyUserDTO companyUserDTO : companyUserDTOS) {
            // 获取每个员工包含的角色信息
            List<SysRoleDTO> roles = companyUserDTO.getRoles();

            if (!CollectionUtils.isEmpty(roles)) {
                for (SysRoleDTO role : roles) {
                    if (role.getRoleName().startsWith(NcConstant.ADMIN_ROLE_PREFIX)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * 根据entity内的authType挑选认证处理器
     *
     * @param entity
     * @return
     */
    private AuthenticationProcessor chooseAuthenticationProcessor(IntergrationAuthenticationEntity entity) {

        for (AuthenticationProcessor processor : processorList) {
            if (processor.support(entity)) { // 如果返回true，代表由该认证处理器进行处理
                return processor;
            }
        }

        return null;
    }
}